'use strict';

var model = require('../../model/orgnization.js');
var debug = require('debug')('dao_orgnization_impl');
var appUtils = require('../../common/app_utils.js');
var errInfo = require('../../common/err_info.js');
var md5 = appUtils.md5;
var fout = appUtils.formatOutput;
var ferror = appUtils.formatError;


function DaoImpl() {
	var self = this instanceof DaoImpl ? this : new DaoImpl();
	return self;
}

module.exports = new DaoImpl();
var theProto = DaoImpl.prototype;
/*
{
"name":"root",
"description":"总部",
"path":[]
}

{
"name":"tianhe", 
"description":"总部->华东区->广东省->广州市->天河区",
"path":[root, zone_east_china, province_gd, city_gz, section_tianhe]
}

*/

theProto.create = function(json) {
	debug('create ', json);

	var self = this;
	return self.checkParent(json)

	.then(function(result) {
		// debug('check parent result, ', result);
		if (result.errCode !== 0) return result;
		return self.checkSame(json);
	})

	// check same node result
	.then(function(result) {
		// debug('check same orgnization, result=', result);
		if (result.errCode !== 0) return result;
		if (result.result.length > 0) return ferror(errInfo.E2500);
		return self.createSingle(json);
	})

	.catch(function(err) {
		return err;
	})
};

// create a tree, isRoot must be true
// {
// 	"name": "xx",
// 	"description": "xx",
// 	"isRoot": "true",
// 	"sub": [{
// 		"name": "xx",
// 		"description": "xx",
// 		"sub": []
// 	}, {
// 		"name": "xx",
// 		"description": "xx",
// 		"sub": []
// 	}]
// }
theProto.createTree = function(json) {
	if (!json.isRoot) {
		return new Promise(function(f, r) {
			ferror(errInfo.E25023)
		});
	};

	var list = Array.isArray(json) ? json : [json];
	var self = this;
	var saved = [];

	function saveNext() {
		debug('saveNext');
		if (list.length === 0) {
			debug('saveNext done');
			return new Promise(function(f, r) {
				f(fout(saved));
			});
		}

		var item = list.shift();
		var itemChild = item.sub || [];
		var entiry = {
			name: item.name,
			description: item.description,
			isRoot: item.isRoot,
			path: item.path || [],
			vendors: item.vendors || [],
		};
		debug('saveNext ', entiry);
		// return self.create(entiry)
		return self.createSingle(entiry)

		.then(function(result) {
			if (result.errCode !== 0) return result;
			var save
			saved.push(result.result);
			// path = parent.path , parent._id
			var path = result.result.path || [];
			path = path.concat(result.result._id);
			itemChild.forEach(function(child) {
				child.path = path;
				list.push(child);
			});

			return saveNext();
		});
	}

	return this.getRootNodeId().then(function(result) {
		if (result.errCode !== 0) return result;
		if (result.result !== null) return ferror(errInfo.E2502);

		return saveNext();
	});
};



// parent should bu null or a existed node
theProto.checkParent = function(json) {
	// debug('checkParent ', json);
	var path = json.path;
	var parent = null;
	if (path && path.length > 0) {
		parent = path[json.length - 1];
	}

	if (parent) {
		return this.readById(parent);
	}

	if (json.isRoot === 1) { // this is the root node
		return new Promise(function(f, r) {
			f(fout("ok"));
		});
	}

	// parent is null and isRoot = 0, this node should be child of root
	this.orgRootNodeId().then(function(result) {
		if (result.errCode !== 0) return result;
		else json.path = [result.result]; // mount to root node
	});
};

var orgRootNodeId = null;
theProto.getRootNodeId = function() {
	if (orgRootNodeId) {
		return new Promise(function(f, r) {
			f(fout(orgRootNodeId));
		});
	} else {
		return this.findOne({
			query: {
				isRoot: 1
			}
		}).then(function(result) {
			if (result.errCode !== 0) return result;
			if (result.result) {
				orgRootNodeId = result.result._id; // cache
			}
			return fout(orgRootNodeId);
		});
	}
};

// if same path and same name
theProto.checkSame = function(json) {
	var condition = {
		query: {
			name: json.name,
		}
	};

	if (json.path.length > 0) {
		condition.query.path = {
			$all: json.path
		}
	} else {
		condition.query.path = {
			"$size": 0
		}
	}

	return this.read(condition);
};


/*
{
"name":"xxx",
"description":"xxx"
"path":["id1","id2"]
}
*/
theProto.createSingle = function(json) {
	var item = new model({
		name: json.name,
		description: json.description || '',
		path: json.path,
		isRoot: json.isRoot
	});

	return item.save()

	.then(function(result) {
		return fout(result);
	}, function(err) {
		return ferror(errInfo.E1000, 'mongodb error: ' + err.errmsg);
	});
};

// json {query:{}}
theProto.read = function(json) {
	return model.find(json.query || {}, json.projection || __defaultProject).exec()

	.then(function(result) {
		return fout(result);
	}, function(err) {
		return ferror(errInfo.E1000, 'mongodb error: ' + err.errmsg);
	});
};

theProto.findOne = function(json) {
	return model.findOne(json.query || {}, json.projection || __defaultProject).exec()

	.then(function(result) {
		return fout(result);
	}, function(err) {
		return ferror(errInfo.E1000, 'mongodb error: ' + err.errmsg);
	});
};

theProto.readById = function(id) {
	return model.findOne({
		_id: id
	}, __defaultProject).exec()

	.then(function(result) {
		if (result.length === 0) return ferror(errInfo.E2501);
		return fout(result);
	}, function(err) {
		return ferror(errInfo.E1000, 'mongodb error: ' + err.errmsg);
	});
};

// json: {query:q, update:u}
theProto.update = function(json) {
	return model.update(json.query, json.update).exec()

	.then(function(result) {
		debug('update,=', result);
		return fout(result);
	}, function(err) {
		return ferror(errInfo.E1000, 'mongodb error: ' + err.errmsg);
	});
};


var __defaultProject = {
	// _id: 0,
	__v: 0,
	updatedAt: 0,
	createdAt: 0,
	description: 0
};